アクセスキー払い出しの承認ワークフローを作ってみた
こんにちは、AWS 事業本部の平木です!
皆さんはアクセスキーを適切に管理していますか?
アクセスキーは極力使用しないことが好ましいですが、どうしても使用しないといけないケースがあるのは事実です。
ただしアクセスキーの使用を許容しても、簡単に払い出されては困る。
そんな悩みを解消するためにアクセスキー払い出しの承認ワークフローの構成を考えてみました。
アーキテクチャ
アクセスキーの払い出しに承認ワークフローを設けるために今回は、AWS Systems Manager Automation を活用します。
SSM Automaion を活用することで SSM ドキュメントに記載した内容の自動実行が可能になります。
詳しくはこちらのブログをご参照ください。
実際に今回考えてみたアーキテクチャは以下です。
流れとしては、
- ① SSM Automation に申請者(requester)からの申請を受け付ける
- ② 申請内容を承認者(approver)に通知
- ③ 承認者が承認か拒否かの回答
- ④ 承認された場合は、後続の Lambda を呼び出し
- ④' 拒否された場合は、拒否した通知を送付
- ⑤ 呼び出された Lambda は、申請内容に基づいて対象の IAM ユーザーのアクセスキーを払い出し
- ⑥ Lambda の処理の中で取得したアクセスキーとシークレットアクセスキーを Secret Manager に保管
- ⑦ Lambda の処理の中で Secret Manager に保管成功した旨を通知
- アクセスキーが既に 2 つ払い出されている場合は、払い出しに失敗した旨を通知
今回は最終的な結果を承認者に通知する仕組みとしていますが、申請者毎に SNS トピックを作成したり SES を活用することで申請者に承認または拒否の通知を自動化することができます。
やってみた
構築
今回のアーキテクチャを CloudFormation で作成してみます。
コードは以下です。
AWSTemplateFormatVersion: '2010-09-09'
Description: IAM Access Key Request Automation
Parameters:
# 承認者の通知先のメールアドレス
AdminEmail:
Type: String
Description: Email address of the administrator
# 承認用 IAM ユーザー名
ApprovalUser:
Type: String
Description: IAM user to approve access key requests
Resources:
# Lambda の IAM ロール
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Path: /
Policies:
- PolicyName: root
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- Effect: Allow
Action:
- iam:CreateAccessKey
- iam:DeleteAccessKey
- iam:GetUser
- iam:ListAccessKeys
- secretsmanager:CreateSecret
- secretsmanager:DeleteSecret
Resource: '*'
- Effect: Allow
Action:
- sns:Publish
Resource: !Ref NotificationTopic
# アクセスキー処理用 Lambda
AccessKeyRequestFunction:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import boto3
import json
import os
from datetime import datetime
def lambda_handler(event, context):
iam_user = event['iam_user']
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
iam = boto3.client('iam')
sns = boto3.client('sns')
# IAM ユーザーが既に 2 つのアクセスキーを持っているかどうかを確認する
response = iam.list_access_keys(UserName=iam_user)
access_keys = response['AccessKeyMetadata']
if len(access_keys) >= 2:
# 管理者にエラー通知を送信する
message = f"IAM ユーザ「 {iam_user} 」のアクセスキー発行のリクエストに失敗しました.\r\n アクセスキーが既に 2 つ発行されています."
sns.publish(
TopicArn=os.environ['NOTIFICATION_TOPIC'],
Message=message,
Subject='Access Key Request Failed'
)
return {
'status': 'error',
'message': 'IAM user already has 2 access keys'
}
# IAM ユーザーのアクセスキーを作成する
response = iam.create_access_key(UserName=iam_user)
access_key = response['AccessKey']
# AccessKey オブジェクトから必要な属性を抽出する
access_key_data = {
'AccessKeyId': access_key['AccessKeyId'],
'SecretAccessKey': access_key['SecretAccessKey']
}
# アクセスキーデータを階層的な命名規則で Secrets Manager に保存する
secrets_manager = boto3.client('secretsmanager')
secret_name = f"{iam_user}/access-key-{timestamp}"
secrets_manager.create_secret(
Name=secret_name,
SecretString=json.dumps(access_key_data)
)
# 管理者に承認通知を送信する
message = f"IAM ユーザ「 {iam_user} 」のアクセスキー発行のリクエストが承認されました.\r\n アクセスキーは Secrets Manager「 {secret_name} 」に保存されました."
sns.publish(
TopicArn=os.environ['NOTIFICATION_TOPIC'],
Message=message,
Subject='Access Key Created'
)
return {
'status': 'created',
'secret_name': secret_name
}
Handler: index.lambda_handler
Role: !GetAtt LambdaExecutionRole.Arn
Runtime: python3.12
Timeout: 60
Environment:
Variables:
NOTIFICATION_TOPIC: !Ref NotificationTopic
# アクセスキー申請処理用 SSM ドキュメント
AccessKeyRequestAutomation:
Type: AWS::SSM::Document
Properties:
DocumentType: Automation
Content:
schemaVersion: '0.3'
description: Automate IAM access key requests
parameters:
IamUser:
type: String
description: IAM user for which access key is requested
RequestReason:
type: String
mainSteps:
- name: ApprovalStep
action: aws:approve
timeoutSeconds: 86400
onFailure: Abort
inputs:
NotificationArn: !Ref NotificationTopic
Message: IAM ユーザ「 {{ IamUser }} 」のアクセスキー発行をリクエストします. 申請理由は、「{{ RequestReason }}」
MinRequiredApprovals: 1
Approvers:
- !Sub arn:aws:iam::${AWS::AccountId}:user/${ApprovalUser}
- name: CreateAccessKey
action: aws:invokeLambdaFunction
timeoutSeconds: 300
maxAttempts: 1
onFailure: Abort
inputs:
FunctionName: !Ref AccessKeyRequestFunction
Payload: !Sub |
{
"iam_user": "{{IamUser}}"
}
# 通知用 SNS トピック
NotificationTopic:
Type: AWS::SNS::Topic
Properties:
Subscription:
- Endpoint: !Ref AdminEmail
Protocol: email
CloudFormation デプロイ時に入力が必要なパラメータは以下です。
- AdminEmail
- 承認者のメールアドレス
- ApprovalUser
- 承認者の IAM ユーザー名(存在しない場合は事前に IAM ユーザーを作成してください)
検証
構築した環境で実際に申請からアクセスキー払い出しまでやってみました。
事前に、承認者(approver)と申請者(requester)の IAM ユーザーを作成済みです。
※承認者にはAmazonSSMAutomationApproverAccess
(AWS管理ポリシー)の権限が必要なため、必要に応じてアタッチしてください
まずは、Systems Manager のナビゲーションペインから①「オートメーション」を選択し、②「Execute automation」を押下します。
続いて①「Owned by me」から CloudFormation で作成した②SSM ドキュメントを押下します。
続いて「オートメーションを実行する」を押下します。
ここの画面では実際に申請を行います。
Input parameters よりIamUser
でアクセスキーを払い出したい IAM ユーザー名を、RequestReason
では申請理由を記載します。
申請理由は日本語でも問題ありません。
入力が出来たら画面一番下の「Execute」を押下します。
申請者が申請を行うと、承認者へ下記のようにメールが送付されます。
承認者はメールを確認したら承認(Approve)または拒否(Reject)のリンクを選択するかコマンドベースで API 経由でも回答できます。
今回は承認してみます。
※承認者が承認または拒否するには承認者(approver)自身の IAM ユーザーでログインする必要があります。
Approve の右隣のリンクを選択すると次の画面が表示されます。
任意のコメントを入力して「Submit」を押下します。
すると後続の処理が開始し、aws:invokeLambdaFunction
が Success になれば完了です。
アクセスキー発行処理が完了すると承認者へメールが通知されます。
IAM ユーザーを見るとアクセスキーが発行されていることが確認でき、
Secret Manager を確認するとアクセスキーとシークレットアクセスキーが保管されていることが確認できます。
拒否またはアクセスキー取得失敗した場合
承認者が拒否した場合は、処理が失敗します。
続いてアクセスキーが既に 2 つ払い出されている場合には取得失敗し通知のみ実施されます。
こちらは Lambda の処理で判別しているため、SSM Automation 上では Success となります。
おわりに
今回はアクセスキー払い出しの承認ワークフローを作ってみました。
アクセスキーの使用は許容するが、セキュリティを考慮してあまり不用意に発行してほしくないと思う管理者もいらっしゃると思います。
Systems Manager Automation を活用することで比較的手軽に承認フローを作成できるため、ぜひ活用いただければと思います。
この記事がどなたかの役に立つと嬉しいです。